home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
051-075
/
disk_053
/
compiler
/
expr.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-06
|
35KB
|
949 lines
#include <stdio.h>
#include "c.h"
#include "expr.h"
#include "gen.h"
#include "cglbdec.h"
/*
* 68000 C compiler
*
* Copyright 1984, 1985, 1986 Matthew Brandt.
* all commercial rights reserved.
*
* This compiler is intended as an instructive tool for personal use. Any
* use for profit without the written consent of the author is prohibited.
*
* This compiler may be distributed freely for non-commercial use as long
* as this notice stays intact. Please forward any enhancements or questions
* to:
*
* Matthew Brandt
* Box 920337
* Norcross, Ga 30092
*/
TYP stdint = { bt_long, 0, 4, {0, 0}, 0, 0 };
TYP stdchar = {bt_char, 0, 1, {0, 0}, 0, 0 };
TYP stdstring = {bt_pointer, 1, 4, {0, 0}, &stdchar, 0};
TYP stdfunc = {bt_func, 1, 0, {0, 0}, &stdint, 0};
extern TYP *head; /* shared with decl */
/*
* expression evaluation
*
* this set of routines builds a parse tree for an expression.
* no code is generated for the expressions during the build,
* this is the job of the codegen module. for most purposes
* expression() is the routine to call. it will allow all of
* the C operators. for the case where the comma operator is
* not valid (function parameters for instance) call exprnc().
*
* each of the routines returns a pointer to a describing type
* structure. each routine also takes one parameter which is a
* pointer to an expression node by reference (address of pointer).
* the completed expression is returned in this pointer. all
* routines return either a pointer to a valid type or NULL if
* the hierarchy of the next operator is too low or the next
* symbol is not part of an expression.
*/
TYP *expression(); /* forward declaration */
TYP *exprnc(); /* forward declaration */
TYP *unary(); /* forward declaration */
struct enode *makenode(nt, v1, v2)
/*
* build an expression node with a node type of nt and values
* v1 and v2.
*/
enum e_node nt;
char *v1, *v2;
{ struct enode *ep;
ep = xalloc(sizeof(struct enode));
ep->nodetype = nt;
ep->constflag = 0;
ep->v.p[0] = v1;
ep->v.p[1] = v2;
return ep;
}
deref(node,tp)
/*
* build the proper dereference operation for a node using the
* type pointer tp.
*/
struct enode **node;
TYP *tp;
{ switch( tp->type ) {
case bt_char:
*node = makenode(en_b_ref,*node,0);
tp = &stdint;
break;
case bt_short:
case bt_enum:
*node = makenode(en_w_ref,*node,0);
tp = &stdint;
break;
case bt_long:
case bt_pointer:
case bt_unsigned:
*node = makenode(en_l_ref,*node,0);
break;
default:
error(ERR_DEREF);
break;
}
return tp;
}
TYP *nameref(node)
/*
* nameref will build an expression tree that references an
* identifier. if the identifier is not in the global or
* local symbol table then a look-ahead to the next character
* is done and if it indicates a function call the identifier
* is coerced to an external function name. non-value references
* generate an additional level of indirection.
*/
struct enode **node;
{ SYM *sp;
TYP *tp;
sp = gsearch(lastid);
if( sp == 0 ) {
while( isspace(lastch) )
getch();
if( lastch == '(') {
++global_flag;
sp = xalloc(sizeof(SYM));
sp->tp = &stdfunc;
sp->name = litlate(lastid);
sp->storage_class = sc_external;
insert(sp,&gsyms);
--global_flag;
tp = &stdfunc;
*node = makenode(en_nacon,sp->name,0);
(*node)->constflag = 1;
}
else {
tp = 0;
error(ERR_UNDEFINED);
}
}
else {
if( (tp = sp->tp) == 0 ) {
error(ERR_UNDEFINED);
return 0; /* guard against untyped entries */
}
switch( sp->storage_class ) {
case sc_static:
*node = makenode(en_labcon,sp->value.i,0);
(*node)->constflag = 1;
break;
case sc_global:
case sc_external:
*node = makenode(en_nacon,sp->name,0);
(*node)->constflag = 1;
break;
case sc_const:
*node = makenode(en_icon,sp->value.i,0);
(*node)->constflag = 1;
break;
default: /* auto and any errors */
if( sp->storage_class != sc_auto)
error(ERR_ILLCLASS);
*node = makenode(en_autocon,sp->value.i,0);
break;
}
if( tp->val_flag == 0)
tp = deref(node,tp);
}
getsym();
return tp;
}
struct enode *parmlist()
/*
* parmlist will build a list of parameter expressions in
* a function call and return a pointer to the last expression
* parsed. since parameters are generally pushed from right
* to left we get just what we asked for...
*/
{ struct enode *ep1, *ep2;
ep1 = 0;
while( lastst != closepa) {
exprnc( &ep2); /* evaluate a parameter */
ep1 = makenode(en_void,ep2,ep1);
if( lastst != comma)
break;
getsym();
}
return ep1;
}
int castbegin(st)
/*
* return 1 if st in set of [ kw_char, kw_short, kw_long, kw_int,
* kw_float, kw_double, kw_struct, kw_union ]
*/
enum e_sym st;
{ return st == kw_char || st == kw_short || st == kw_int ||
st == kw_long || st == kw_float || st == kw_double ||
st == kw_struct || st == kw_union || st== kw_unsigned;
}
TYP *primary(node)
/*
* primary will parse a primary expression and set the node pointer
* returning the type of the expression parsed. primary expressions
* are any of:
* id
* constant
* string
* ( expression )
* primary[ expression ]
* primary.id
* primary->id
* primary( parameter list )
*/
struct enode **node;
{ struct enode *pnode, *qnode, *rnode;
SYM *sp;
TYP *tptr;
switch( lastst ) {
case id:
tptr = nameref(&pnode);
break;
case iconst:
tptr = &stdint;
pnode = makenode(en_icon,ival,0);
pnode->constflag = 1;
getsym();
break;
case sconst:
tptr = &stdstring;
pnode = makenode(en_labcon,stringlit(laststr),0);
pnode->constflag = 1;
getsym();
break;
case openpa:
getsym();
if( !castbegin(lastst) ) {
tptr = expression(&pnode);
needpunc(closepa);
}
else { /* cast operator */
decl(); /* do cast declaration */
decl1();
tptr = head;
needpunc(closepa);
if( unary(&pnode) == 0 ) {
error(ERR_IDEXPECT);
tptr = 0;
}
}
break;
default:
return 0;
}
for(;;) {
switch( lastst ) {
case openbr: /* build a subscript reference */
if( tptr->type != bt_pointer )
error(ERR_NOPOINTER);
else
tptr = tptr->btp;
getsym();
qnode = makenode(en_icon,tptr->size,0);
qnode->constflag = 1;
expression(&rnode);
/*
* we could check the type of the expression here...
*/
qnode = makenode(en_mul,qnode,rnode);
qnode->constflag = rnode->constflag &&
qnode->v.p[0]->constflag;
pnode = makenode(en_add,qnode,pnode);
pnode->constflag = qnode->constflag &&
pnode->v.p[1]->constflag;
if( tptr->val_flag == 0 )
tptr = deref(&pnode,tptr);
needpunc(closebr);
break;
case pointsto:
if( tptr->type != bt_pointer )
error(ERR_NOPOINTER);
else
tptr = tptr->btp;
if( tptr->val_flag == 0 )
pnode = makenode(en_l_ref,pnode,0);
/*
* fall through to dot operation
*/
case dot:
getsym(); /* past -> or . */
if( lastst != id )
error(ERR_IDEXPECT);
else {
sp = search(lastid,tptr->lst.head);
if( sp == 0 )
error(ERR_NOMEMBER);
else {
tptr = sp->tp;
qnode = makenode(en_icon,sp->value.i,0);
qnode->constflag = 1;
pnode = makenode(en_add,pnode,qnode);
pnode->constflag = pnode->v.p[0]->constflag;
if( tptr->val_flag == 0 )
tptr = deref(&pnode,tptr);
}
getsym(); /* past id */
}
break;
case openpa: /* function reference */
getsym();
if( tptr->type != bt_func &&
tptr->type != bt_ifunc )
error(ERR_NOFUNC);
else
tptr = tptr->btp;
pnode = makenode(en_fcall,pnode,parmlist());
needpunc(closepa);
break;
default:
goto fini;
}
}
fini: *node = pnode;
return tptr;
}
int lvalue(node)
/*
* this function returns true if the node passed is an lvalue.
* this can be qualified by the fact that an lvalue must have
* one of the dereference operators as it's top node.
*/
struct enode *node;
{ switch( node->nodetype ) {
case en_b_ref:
case en_w_ref:
case en_l_ref:
case en_ub_ref:
case en_uw_ref:
case en_ul_ref:
return 1;
case en_cbl:
case en_cwl:
case en_cbw:
return lvalue(node->v.p[0]);
}
return 0;
}
TYP *unary(node)
/*
* unary evaluates unary expressions and returns the type of the
* expression evaluated. unary expressions are any of:
*
* primary
* primary++
* primary--
* !unary
* ~unary
* ++unary
* --unary
* -unary
* *unary
* &unary
* (typecast)unary
* sizeof(typecast)
*
*/
struct enode **node;
{ TYP *tp, *tp1;
struct enode *ep1, *ep2;
int flag, i;
flag = 0;
switch( lastst ) {
case autodec:
flag = 1;
/* fall through to common increment */
case autoinc:
getsym();
tp = unary(&ep1);
if( tp == 0 ) {
error(ERR_IDEXPECT);
return 0;
}
if( lvalue(ep1)) {
if( tp->type == bt_pointer )
ep2 = makenode(en_icon,tp->btp->size,0);
else
ep2 = makenode(en_icon,1,0);
ep2->constflag = 1;
ep1 = makenode(flag ? en_assub : en_asadd,ep1,ep2);
}
else
error(ERR_LVALUE);
break;
case minus:
getsym();
tp = unary(&ep1);
if( tp == 0 ) {
error(ERR_IDEXPECT);
return 0;
}
ep1 = makenode(en_uminus,ep1,0);
ep1->constflag = ep1->v.p[0]->constflag;
break;
case not:
getsym();
tp = unary(&ep1);
if( tp == 0 ) {
error(ERR_IDEXPECT);
return 0;
}
ep1 = makenode(en_not,ep1,0);
ep1->constflag = ep1->v.p[0]->constflag;
break;
case compl:
getsym();
tp = unary(&ep1);
if( tp == 0 ) {
error(ERR_IDEXPECT);
return 0;
}
ep1 = makenode(en_compl,ep1,0);
ep1->constflag = ep1->v.p[0]->constflag;
break;
case star:
getsym();
tp = unary(&ep1);
if( tp == 0 ) {
error(ERR_IDEXPECT);
return 0;
}
if( tp->btp == 0 )
error(ERR_DEREF);
else
tp = tp->btp;
if( tp->val_flag == 0 )
tp = deref(&ep1,tp);
break;
case and:
getsym();
tp = unary(&ep1);
if( tp == 0 ) {
error(ERR_IDEXPECT);
return 0;
}
if( lvalue(ep1))
ep1 = ep1->v.p[0];
tp1 = xalloc(sizeof(TYP));
tp1->size = 4;
tp1->type = bt_pointer;
tp1->btp = tp;
tp1->val_flag = 0;
tp1->lst.head = 0;
tp1->sname = 0;
tp = tp1;
break;
case kw_sizeof:
getsym();
needpunc(openpa);
decl();
decl1();
if( head != 0 )
ep1 = makenode(en_icon,head->size,0);
else {
error(ERR_IDEXPECT);
ep1 = makenode(en_icon,1,0);
}
ep1->constflag = 1;
tp = &stdint;
needpunc(closepa);
break;
default:
tp = primary(&ep1);
if( tp != 0 ) {
if( tp->type == bt_pointer )
i = tp->btp->size;
else
i = 1;
if( lastst == autoinc) {
if( lvalue(ep1) )
ep1 = makenode(en_ainc,ep1,i);
else
error(ERR_LVALUE);
getsym();
}
else if( lastst == autodec ) {
if( lvalue(ep1) )
ep1 = makenode(en_adec,ep1,i);
else
error(ERR_LVALUE);
getsym();
}
}
break;
}
*node = ep1;
return tp;
}
TYP *forcefit(node1,tp1,node2,tp2)
/*
* forcefit will coerce the nodes passed into compatable
* types and return the type of the resulting expression.
*/
struct enode **node1, **node2;
TYP *tp1, *tp2;
{ switch( tp1->type ) {
case bt_char:
case bt_short:
case bt_long:
if( tp2->type == bt_long ||
tp2->type == bt_short ||
tp2->type == bt_char)
return &stdint;
else if( tp2->type == bt_pointer ||
tp2->type == bt_unsigned )
return tp2;
break;
case bt_pointer:
if( isscalar(tp2) || tp2->type == bt_pointer)
return tp1;
break;
case bt_unsigned:
if( tp2->type == bt_pointer )
return tp2;
else if( isscalar(tp2) )
return tp1;
break;
}
error( ERR_MISMATCH );
return tp1;
}
int isscalar(tp)
/*
* this function returns true when the type of the argument is
* one of char, short, unsigned, or long.
*/
TYP *tp;
{ return tp->type == bt_char ||
tp->type == bt_short ||
tp->type == bt_long ||
tp->type == bt_unsigned;
}
TYP *multops(node)
/*
* multops parses the multiply priority operators. the syntax of
* this group is:
*
* unary
* multop * unary
* multop / unary
* multop % unary
*/
struct enode **node;
{ struct enode *ep1, *ep2;
TYP *tp1, *tp2;
enum e_sym oper;
tp1 = unary(&ep1);
if( tp1 == 0 )
return 0;
while( lastst == star || lastst == divide || lastst == modop ) {
oper = lastst;
getsym(); /* move on to next unary op */
tp2 = unary(&ep2);
if( tp2 == 0 ) {
error(ERR_IDEXPECT);
*node = ep1;
return tp1;
}
tp1 = forcefit(&ep1,tp1,&ep2,tp2);
switch( oper ) {
case star:
if( tp1->type == bt_unsigned )
ep1 = makenode(en_umul,ep1,ep2);
else
ep1 = makenode(en_mul,ep1,ep2);
break;
case divide:
if( tp1->type == bt_unsigned )
ep1 = makenode(en_udiv,ep1,ep2);
else
ep1 = makenode(en_div,ep1,ep2);
break;
case modop:
if( tp1->type == bt_unsigned )
ep1 = makenode(en_umod,ep1,ep2);
else
ep1 = makenode(en_mod,ep1,ep2);
break;
}
ep1->constflag = ep1->v.p[0]->constflag &&
ep1->v.p[1]->constflag;
}
*node = ep1;
return tp1;
}
TYP *addops(node)
/*
* addops handles the addition and subtraction operators.
*/
struct enode **node;
{ struct enode *ep1, *ep2, *ep3;
TYP *tp1, *tp2;
int oper, i;
tp1 = multops(&ep1);
if( tp1 == 0 )
return 0;
while( lastst == plus || lastst == minus ) {
oper = (lastst == plus);
getsym();
tp2 = multops(&ep2);
if( tp2 == 0 ) {
error(ERR_IDEXPECT);
*node = ep1;
return tp1;
}
if( tp1->type == bt_pointer ) {
tp2 = forcefit(0,&stdint,&ep2,tp2);
ep3 = makenode(en_icon,tp1->btp->size,0);
ep3->constflag = 1;
ep2 = makenode(en_mul,ep3,ep2);
ep2->constflag = ep2->v.p[1]->constflag;
}
else if( tp2->type == bt_pointer ) {
tp1 = forcefit(0,&stdint,&ep1,tp1);
ep3 = makenode(en_icon,tp2->btp->size,0);
ep3->constflag = 1;
ep1 = makenode(en_mul,ep3,ep1);
ep1->constflag = ep1->v.p[1]->constflag;
}
tp1 = forcefit(&ep1,tp1,&ep2,tp2);
ep1 = makenode( oper ? en_add : en_sub,ep1,ep2);
ep1->constflag = ep1->v.p[0]->constflag &&
ep1->v.p[1]->constflag;
}
*node = ep1;
return tp1;
}
TYP *shiftop(node)
/*
* shiftop handles the shift operators << and >>.
*/
struct enode **node;
{ struct enode *ep1, *ep2;
TYP *tp1, *tp2;
int oper;
tp1 = addops(&ep1);
if( tp1 == 0)
return 0;
while( lastst == lshift || lastst == rshift) {
oper = (lastst == lshift);
getsym();
tp2 = addops(&ep2);
if( tp2 == 0 )
error(ERR_IDEXPECT);
else {
tp1 = forcefit(&ep1,tp1,&ep2,tp2);
ep1 = makenode(oper ? en_lsh : en_rsh,ep1,ep2);
ep1->constflag = ep1->v.p[0]->constflag &&
ep1->v.p[1]->constflag;
}
}
*node = ep1;
return tp1;
}
TYP *relation(node)
/*
* relation handles the relational operators < <= > and >=.
*/
struct enode **node;
{ struct enode *ep1, *ep2;
TYP *tp1, *tp2;
int nt;
tp1 = shiftop(&ep1);
if( tp1 == 0 )
return 0;
for(;;) {
switch( lastst ) {
case lt:
if( tp1->type == bt_unsigned )
nt = en_ult;
else
nt = en_lt;
break;
case gt:
if( tp1->type == bt_unsigned )
nt = en_ugt;
else
nt = en_gt;
break;
case leq:
if( tp1->type == bt_unsigned )
nt = en_ule;
else
nt = en_le;
break;
case geq:
if( tp1->type == bt_unsigned )
nt = en_uge;
else
nt = en_ge;
break;
default:
goto fini;
}
getsym();
tp2 = shiftop(&ep2);
if( tp2 == 0 )
error(ERR_IDEXPECT);
else {
tp1 = forcefit(&ep1,tp1,&ep2,tp2);
ep1 = makenode(nt,ep1,ep2);
ep1->constflag = ep1->v.p[0]->constflag &&
ep1->v.p[1]->constflag;
}
}
fini: *node = ep1;
return tp1;
}
TYP *equalops(node)
/*
* equalops handles the equality and inequality operators.
*/
struct enode **node;
{ struct enode *ep1, *ep2;
TYP *tp1, *tp2;
int oper;
tp1 = relation(&ep1);
if( tp1 == 0 )
return 0;
while( lastst == eq || lastst == neq ) {
oper = (lastst == eq);
getsym();
tp2 = relation(&ep2);
if( tp2 == 0 )
error(ERR_IDEXPECT);
else {
tp1 = forcefit(&ep1,tp1,&ep2,tp2);
ep1 = makenode( oper ? en_eq : en_ne,ep1,ep2);
ep1->constflag = ep1->v.p[0]->constflag &&
ep1->v.p[1]->constflag;
}
}
*node = ep1;
return tp1;
}
TYP *binop(node,xfunc,nt,sy)
/*
* binop is a common routine to handle all of the legwork and
* error checking for bitand, bitor, bitxor, andop, and orop.
*/
struct enode **node;
TYP *(*xfunc)();
int nt;
enum e_sym sy;
{ struct enode *ep1, *ep2;
TYP *tp1, *tp2;
tp1 = (*xfunc)(&ep1);
if( tp1 == 0 )
return 0;
while( lastst == sy ) {
getsym();
tp2 = (*xfunc)(&ep2);
if( tp2 == 0 )
error(ERR_IDEXPECT);
else {
tp1 = forcefit(&ep1,tp1,&ep2,tp2);
ep1 = makenode(nt,ep1,ep2);
ep1->constflag = ep1->v.p[0]->constflag &&
ep1->v.p[1]->constflag;
}
}
*node = ep1;
return tp1;
}
TYP *bitand(node)
/*
* the bitwise and operator...
*/
struct enode **node;
{ return binop(node,equalops,en_and,and);
}
TYP *bitxor(node)
struct enode **node;
{ return binop(node,bitand,en_xor,uparrow);
}
TYP *bitor(node)
struct enode **node;
{ return binop(node,bitxor,en_or,or);
}
TYP *andop(node)
struct enode **node;
{ return binop(node,bitor,en_land,land);
}
TYP *orop(node)
struct enode **node;
{ return binop(node,andop,en_lor,lor);
}
TYP *conditional(node)
/*
* this routine processes the hook operator.
*/
struct enode **node;
{ TYP *tp1, *tp2, *tp3;
struct enode *ep1, *ep2, *ep3;
tp1 = orop(&ep1); /* get condition */
if( tp1 == 0 )
return 0;
if( lastst == hook ) {
getsym();
if( (tp2 = conditional(&ep2)) == 0) {
error(ERR_IDEXPECT);
goto cexit;
}
needpunc(colon);
if( (tp3 = conditional(&ep3)) == 0) {
error(ERR_IDEXPECT);
goto cexit;
}
tp1 = forcefit(&ep2,tp2,&ep3,tp3);
ep2 = makenode(en_void,ep2,ep3);
ep1 = makenode(en_cond,ep1,ep2);
}
cexit: *node = ep1;
return tp1;
}
TYP *asnop(node)
/*
* asnop handles the assignment operators. currently only the
* simple assignment is implemented.
*/
struct enode **node;
{ struct enode *ep1, *ep2, *ep3;
TYP *tp1, *tp2;
int op;
tp1 = conditional(&ep1);
if( tp1 == 0 )
return 0;
for(;;) {
switch( lastst ) {
case assign:
op = en_assign;
ascomm: getsym();
tp2 = asnop(&ep2);
ascomm2: if( tp2 == 0 || !lvalue(ep1) )
error(ERR_LVALUE);
else {
tp1 = forcefit(&ep1,tp1,&ep2,tp2);
ep1 = makenode(op,ep1,ep2);
}
break;
case asplus:
op = en_asadd;
ascomm3: tp2 = asnop(&ep2);
if( tp1->type == bt_pointer ) {
ep3 = makenode(en_icon,tp1->btp->size,0);
ep2 = makenode(en_mul,ep2,ep3);
}
goto ascomm;
case asminus:
op = en_assub;
goto ascomm3;
case astimes:
op = en_asmul;
goto ascomm;
case asdivide:
op = en_asdiv;
goto ascomm;
case asmodop:
op = en_asmod;
goto ascomm;
case aslshift:
op = en_aslsh;
goto ascomm;
case asrshift:
op = en_asrsh;
goto ascomm;
case asand:
op = en_asand;
goto ascomm;
case asor:
op = en_asor;
goto ascomm;
default:
goto asexit;
}
}
asexit: *node = ep1;
return tp1;
}
TYP *exprnc(node)
/*
* evaluate an expression where the comma operator is not legal.
*/
struct enode **node;
{ TYP *tp;
tp = asnop(node);
if( tp == 0 )
*node = 0;
return tp;
}
TYP *commaop(node)
/*
* evaluate the comma operator. comma operators are kept as
* void nodes.
*/
struct enode **node;
{ TYP *tp1;
struct enode *ep1, *ep2;
tp1 = asnop(&ep1);
if( tp1 == 0 )
return 0;
if( lastst == comma ) {
tp1 = commaop(&ep2);
if( tp1 == 0 ) {
error(ERR_IDEXPECT);
goto coexit;
}
ep1 = makenode(en_void,ep1,ep2);
}
coexit: *node = ep1;
return tp1;
}
TYP *expression(node)
/*
* evaluate an expression where all operators are legal.
*/
struct enode **node;
{ TYP *tp;
tp = commaop(node);
if( tp == 0 )
*node = 0;
return tp;
}